home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / cmds / ps / ps.c.new < prev    next >
Encoding:
Text File  |  1989-06-11  |  31.2 KB  |  1,218 lines

  1. /* 
  2.  * ps.c --
  3.  *
  4.  *    This file contains a program that will print out process
  5.  *    status information for one or more processes.  See the
  6.  *    man page for details on what it does.
  7.  *
  8.  * Copyright 1988 Regents of the University of California
  9.  * Permission to use, copy, modify, and distribute this
  10.  * software and its documentation for any purpose and without
  11.  * fee is hereby granted, provided that the above copyright
  12.  * notice appear in all copies.  The University of California
  13.  * makes no representations about the suitability of this
  14.  * software for any purpose.  It is provided "as is" without
  15.  * express or implied warranty.
  16.  */
  17.  
  18. #ifndef lint
  19. static char rcsid[] = "$Header: /a/newcmds/ps/RCS/ps.c,v 1.15 89/03/24 16:01:07 ouster Exp Locker: douglis $ SPRITE (Berkeley)";
  20. #endif not lint
  21.  
  22. #include <ctype.h>
  23. #include <hash.h>
  24. #include <host.h>
  25. #include <option.h>
  26. #include <proc.h>
  27. #include <pwd.h>
  28. #include <spriteTime.h>
  29. #include <status.h>
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <sys/ioctl.h>
  34. #include <vm.h>
  35.  
  36. /*
  37.  * Process status information may be printed in any of several ways,
  38.  * selected by command-line switches.  For each way there is a procedure
  39.  * that is called to print out in that format.  The following table
  40.  * identifies all such procedures:
  41.  */
  42.  
  43. extern void UpdateMigInfo();
  44. extern void PrintIDs(), PrintLong(), PrintMigration();
  45. extern void PrintShort(), PrintSignals(), PrintVM();
  46.  
  47. void (*(printProc[]))() = {
  48.     PrintShort,
  49.     PrintLong,
  50.     PrintIDs,
  51.     PrintVM,
  52.     PrintMigration,
  53.     PrintSignals,
  54. };
  55.  
  56. /*
  57.  * Indexes into printProc:
  58.  */
  59.  
  60. #define SHORT    0
  61. #define    LONG    1
  62. #define IDS    2
  63. #define VM    3
  64. #define MIG    4
  65. #define SIGS    5
  66.  
  67. int printIndex = 0;
  68.  
  69. /*
  70.  * Corresponding to each of the printing styles above, there is a
  71.  * corresponding sort procedure that is used as an argument to qsort
  72.  * in order to sort the process table entries in a particular way.
  73.  * 0 means don't sort:  just print them in table order.
  74.  */
  75.  
  76. extern int AgeSort(), UsageSort();
  77.  
  78. int (*(sortProc[]))() = {
  79.     AgeSort,
  80.     UsageSort,
  81.     0,
  82.     0,
  83.     0,
  84.     0
  85. };
  86.  
  87. /*
  88.  * Flags set by command-line options:
  89.  */
  90.  
  91. int aFlag =        0;    /* Non-zero means consider processes for
  92.                  * all users. */
  93. int AFlag =        0;    /* Non-zero means even consider dead procs. */
  94. int dFlag =        0;    /* Non-zero means only print info for
  95.                  * processes in debug state. */
  96. int kFlag =        0;    /* Non-zero means print out kernel processes
  97.                  * too. */
  98. int mFlag =        0;    /* Non-zero means only print out info for
  99.                  * processes in migrated state, or foreign
  100.                  * processes. */
  101. int lFlag =        0;    /* Non-zero means only print out info for
  102.                  * local processes. */
  103. int lineWidth =        80;    /* Number of chars in each printed line. */
  104.  
  105. /*
  106.  * The table below describes the various command-line options that
  107.  * are understood by this program:
  108.  */
  109.  
  110. Option optionArray[] = {
  111.     OPT_DOC,        (char *) NULL,    (char *) NULL,
  112.         "This program prints out process status information.\n Synopsis:  \"ps [switches] [pid pid ...]\"\n Command-line switches are:",
  113.     OPT_TRUE,        "a",        (char *) &aFlag,
  114.         "Print info for all users' processes\n\t\tDefault: only current user's processes",
  115.     OPT_TRUE,        "A",        (char *) &AFlag,
  116.         "Print info for absolutely all user processes, even dead ones",
  117.     OPT_TRUE,        "d",        (char *) &dFlag,
  118.         "Print out only processes in DEBUG state",
  119.     OPT_CONSTANT(IDS),    "i",        (char *) &printIndex,
  120.         "Print out various process ids",
  121.     OPT_TRUE,        "k",        (char *) &kFlag,
  122.         "Print out kernel server processes as well as user processes",
  123.     OPT_TRUE,        "l",        (char *) &lFlag,
  124.         "Print out only local processes, not migrated ones",
  125.     OPT_TRUE,        "m",        (char *) &mFlag,
  126.         "Print out only processes that are foreign or migrated",
  127.     OPT_CONSTANT(SIGS),    "s",        (char *) &printIndex,
  128.         "Print out information about signals",
  129.     OPT_CONSTANT(LONG),    "u",        (char *) &printIndex,
  130.         "Print info in longer \"user-oriented\" form",
  131.     OPT_CONSTANT(VM),    "v",        (char *) &printIndex,
  132.         "Print virtual memory information",
  133.     OPT_INT,        "w",        (char *) &lineWidth,
  134.         "Next argument holds line width for output",
  135.     OPT_CONSTANT(MIG),    "M",        (char *) &printIndex,
  136.         "Print out migration information for migrated processes",
  137. };
  138.  
  139. /*
  140.  * The following type is used to associate a process control block an
  141.  * its argument string, so that during various sorts the two can be kept
  142.  * properly associated.  Furthermore, when following migrated processes
  143.  * we lose information about what processes are migrated and what aren't,
  144.  * so we associate a host with each one.  vm-related calls to get additional
  145.  * information must be done relative to the host on which the process
  146.  * is running.
  147.  */
  148.  
  149. typedef struct {
  150.     Proc_PCBInfo *infoPtr;
  151.     Proc_PCBArgString *argString;
  152.     int host;
  153. } ControlBlock;
  154.  
  155. /*
  156.  * Miscellaneous global variables:
  157.  */
  158.  
  159. int lastPCB;        /* Set to non-zero before processing last
  160.              * control block (allows print procs to print
  161.              * totals, if they want). */
  162.  
  163. extern int     timer_IntOneSecond; /* ticks per second */
  164.  
  165. /*
  166.  *----------------------------------------------------------------------
  167.  *
  168.  * main --
  169.  *
  170.  *    The main program for ps.
  171.  *
  172.  * Results:
  173.  *    None.
  174.  *
  175.  * Side effects:
  176.  *    Prints information on standard output.
  177.  *
  178.  *----------------------------------------------------------------------
  179.  */
  180.  
  181. main(argc, argv)
  182.     int argc;        /* Number of command-line arguments. */
  183.     char **argv;    /* Values of command-line arguments. */
  184. {
  185.     int i, pcbsUsed;
  186.     ReturnStatus status;
  187.     struct winsize winsize;
  188.     int host;
  189.  
  190.     /*
  191.      * Find out how big the lines are, for formatting output, then
  192.      * parse options.
  193.      */
  194.  
  195.     if ((ioctl(fileno(stdout), TIOCGWINSZ, (char *) &winsize) == 0)
  196.         && (winsize.ws_col != 0)) {
  197.     lineWidth = winsize.ws_col;
  198.     } else {
  199.     char buf[1024];
  200.     char *termEnv;
  201.  
  202.     termEnv = getenv("TERM");
  203.     if (termEnv == 0) {
  204.         termEnv = "";
  205.     }
  206.     if (tgetent(buf, termEnv) == 1) {
  207.         i = tgetnum("co");
  208.         if (i > 0) {
  209.         lineWidth = i;
  210.         }
  211.     }
  212.     }
  213.  
  214.     argc = Opt_Parse(argc, argv, optionArray, Opt_Number(optionArray),
  215.         OPT_ALLOW_CLUSTERING);
  216.  
  217.     /*
  218.      * If particular process ids were given, then only print them.
  219.      * Otherwise look at all the processes in the system.
  220.      */
  221.  
  222.     if (argc > 1) {
  223.     for (i = 1; i < argc; i++) {
  224.         int pid;
  225.         Proc_PCBInfo info;
  226.         Proc_PCBInfo migInfo;
  227.         Proc_PCBArgString argString;
  228.         char *endPtr;
  229.  
  230.         pid = strtoul(argv[i], &endPtr, 16);
  231.         if (endPtr == argv[i]) {
  232.         fprintf(stderr, "Bad process id \"%s\";  ignoring.\n", argv[i]);
  233.         continue;
  234.         }
  235.         status = Proc_GetPCBInfo(Proc_PIDToIndex(pid),
  236.                      Proc_PIDToIndex(pid), PROC_MY_HOSTID,
  237.                      sizeof(info),
  238.                      &info, &argString, &pcbsUsed);
  239.         if (status != SUCCESS) {
  240.         fprintf(stderr, "Couldn't find pid \"%s\": %s.\n", argv[i],
  241.             Stat_GetMsg(status));
  242.         continue;
  243.         }
  244.         /*
  245.          * The process we got info for may not be the one that was
  246.          * requested (different generation numbers);  check to be sure.
  247.          */
  248.  
  249.         if (pid != info.processID) {
  250.         fprintf(stderr, "Pid %s not found.\n", argv[i]);
  251.         continue;
  252.         }
  253.         host = 0;
  254.         if (printIndex != MIG && info.state == PROC_MIGRATED) {
  255.         status = Proc_GetPCBInfo(Proc_PIDToIndex(info.peerProcessID),
  256.                      Proc_PIDToIndex(info.peerProcessID),
  257.                      info.peerHostID,
  258.                      sizeof(migInfo), &migInfo, 
  259. #ifdef notdef
  260.                      /*
  261.                       * bug in kernel,
  262.                       * checks NIL instead of NULL.
  263.                       */
  264.                      (Proc_PCBArgString *) NULL,
  265. #else
  266.                      &argString,
  267. #endif
  268.                      (int *) NULL);
  269.         if (status != SUCCESS) {
  270.             fprintf(stderr, "Couldn't find migrated pid \"%x\": %s.\n",
  271.                 info.peerProcessID, Stat_GetMsg(status));
  272.         } else{
  273.             UpdateMigInfo(&migInfo, &info);
  274.             host = info.peerHostID;
  275.         }
  276.         }
  277.         if (i == (argc-1)) {
  278.         lastPCB = 1;
  279.         }
  280.         (*(printProc[printIndex]))(&info, &argString, host);
  281.     }
  282.     } else {
  283. #define NUM_PCBS 256
  284.     Proc_PCBInfo infos[NUM_PCBS];
  285.     Proc_PCBInfo migInfo;
  286.     ControlBlock blocks[NUM_PCBS];
  287.     Proc_PCBArgString argStrings[NUM_PCBS];
  288.     int numToPrint, uid;
  289.     register Proc_PCBInfo *infoPtr;
  290.  
  291.     /*
  292.      * Dump the entire process table into our memory.
  293.      */
  294.  
  295.     status = Proc_GetPCBInfo(0, NUM_PCBS-1, PROC_MY_HOSTID,
  296.                  sizeof(Proc_PCBInfo),
  297.                  infos, argStrings, &pcbsUsed);
  298.     if (status != SUCCESS) {
  299.         fprintf(stderr, "Couldn't read process table: %s\n",
  300.             Stat_GetMsg(status));
  301.         exit(1);
  302.     }
  303.  
  304.     /*
  305.      * Collect info into blocks suitable for sorting.  Along the way,
  306.      * filter out irrelevant processes.
  307.      */
  308.  
  309.     uid = geteuid();
  310.     for (i = 0, numToPrint = 0, infoPtr = infos; i < pcbsUsed;
  311.         i++, infoPtr++) {
  312.         if (infoPtr->state == PROC_UNUSED) {
  313.         if (!AFlag) {
  314.             continue;
  315.         }
  316.         goto keepThisProc;
  317.         }
  318.         if (infoPtr->genFlags & PROC_KERNEL) {
  319.         if (!kFlag) {
  320.             continue;
  321.         }
  322.         goto keepThisProc;
  323.         }
  324.         if ((!aFlag) && (uid != infoPtr->effectiveUserID)) {
  325.            continue;
  326.         }
  327.         if (dFlag) {
  328.         if ((infoPtr->state != PROC_SUSPENDED) || !(infoPtr->genFlags
  329.             & (PROC_DEBUGGED | PROC_ON_DEBUG_LIST))) {
  330.             continue;
  331.         }
  332.         }
  333.         if (mFlag &&
  334.         !((infoPtr->genFlags & PROC_FOREIGN)
  335.           || (infoPtr->state == PROC_MIGRATED))) {
  336.         continue;
  337.         }
  338.         if (lFlag && (infoPtr->state == PROC_MIGRATED)) {
  339.         continue;
  340.         }
  341.  
  342.         keepThisProc:
  343.         /*
  344.          * When not printing migration info, follow migrated processes.
  345.          * For now, just do this for short listings since we can't
  346.          * follow vm info.
  347.          */
  348.         host = 0;
  349.         if (printIndex == SHORT &&  infoPtr->state == PROC_MIGRATED) {
  350.         status = Proc_GetPCBInfo(Proc_PIDToIndex(infoPtr->peerProcessID),
  351.                      Proc_PIDToIndex(infoPtr->peerProcessID),
  352.                      infoPtr->peerHostID,
  353.                      sizeof(migInfo), &migInfo, 
  354. #ifdef notdef
  355.                      /*
  356.                       * bug in kernel,
  357.                       * checks NIL instead of NULL.
  358.                       */
  359.                      (Proc_PCBArgString *) NULL,
  360. #else
  361.                      &argStrings[i],
  362. #endif
  363.                      (int *) NULL);
  364.         if (status != SUCCESS) {
  365.             fprintf(stderr, "Couldn't find migrated pid \"%x\": %s.\n",
  366.                 infoPtr->peerProcessID, Stat_GetMsg(status));
  367.         } else {
  368.             UpdateMigInfo(&migInfo, infoPtr);
  369.             host = infoPtr->peerHostID;
  370.         }
  371.         }
  372.         blocks[numToPrint].infoPtr = infoPtr;
  373.         blocks[numToPrint].argString = &argStrings[i];
  374.         blocks[numToPrint].host = host;
  375.         numToPrint++;
  376.     }
  377.  
  378.     /*
  379.          * Sort the processes, if a sorting procedure has been supplied.
  380.      */
  381.  
  382.     if (sortProc[printIndex] != 0) {
  383.         qsort((char *) blocks, numToPrint, sizeof(ControlBlock),
  384.             sortProc[printIndex]);
  385.     }
  386.  
  387.     /*
  388.      * Print them out in order.
  389.      */
  390.  
  391.     for (i = 0; i < numToPrint; i++) {
  392.         if (i == (numToPrint-1)) {
  393.         lastPCB = 1;
  394.         }
  395.         (*(printProc[printIndex]))(blocks[i].infoPtr, blocks[i].argString,
  396.                        blocks[i].host);
  397.     }
  398.     }
  399.     exit(0);
  400. }
  401.  
  402. /*
  403.  *----------------------------------------------------------------------
  404.  *
  405.  * UpdateMigInfo --
  406.  *
  407.  *    Update relevant information for a migrated process using the
  408.  *    PCB info obtained from its current host.
  409.  *
  410.  * Results:
  411.  *    None.
  412.  *
  413.  * Side effects:
  414.  *    Fields are copied from one structure to the other.
  415.  *
  416.  *----------------------------------------------------------------------
  417.  */
  418.  
  419. void
  420. UpdateMigInfo(migPtr, infoPtr)
  421.     Proc_PCBInfo *migPtr;    /* Pointer to control block on current host. */
  422.     Proc_PCBInfo *infoPtr;    /* Pointer to control block on this host. */
  423. {
  424.     register int i;
  425.     
  426.     infoPtr->processor = migPtr->processor;
  427.     infoPtr->state = migPtr->state;    
  428.     infoPtr->genFlags = migPtr->genFlags;
  429.     infoPtr->event = migPtr->event;
  430.     infoPtr->billingRate = migPtr->billingRate;
  431.     infoPtr->recentUsage = migPtr->recentUsage;
  432.     infoPtr->weightedUsage = migPtr->weightedUsage;
  433.     infoPtr->unweightedUsage = migPtr->unweightedUsage;
  434.     infoPtr->kernelCpuUsage = migPtr->kernelCpuUsage;
  435.     infoPtr->userCpuUsage = migPtr->userCpuUsage;
  436.     infoPtr->childKernelCpuUsage = migPtr->childKernelCpuUsage;
  437.     infoPtr->childUserCpuUsage = migPtr->childUserCpuUsage;
  438.     infoPtr->numQuantumEnds = migPtr->numQuantumEnds;
  439.     infoPtr->numWaitEvents = migPtr->numWaitEvents;
  440.     infoPtr->schedQuantumTicks = migPtr->schedQuantumTicks;
  441.     for (i = 0; i < VM_NUM_SEGMENTS; i++) {
  442.     infoPtr->vmSegments[i] = migPtr->vmSegments[i];
  443.     }
  444.     infoPtr->sigHoldMask = migPtr->sigHoldMask;
  445.     infoPtr->sigPendingMask = migPtr->sigPendingMask;
  446.     for (i = 0; i < SIG_NUM_SIGNALS; i++) {
  447.     infoPtr->sigActions[i] = migPtr->sigActions[i];
  448.     }
  449. }
  450.  
  451. /*
  452.  *----------------------------------------------------------------------
  453.  *
  454.  * TimeString --
  455.  *
  456.  *    Given a process control block, return a string indicating how
  457.  *    much CPU time the process has used up.
  458.  *
  459.  * Results:
  460.  *    The return value is a statically-allocated string that holds
  461.  *    the time used by the process, in the format min:sec.
  462.  *
  463.  * Side effects:
  464.  *    None.
  465.  *
  466.  *----------------------------------------------------------------------
  467.  */
  468.  
  469. char *
  470. TimeString(infoPtr)
  471.     Proc_PCBInfo *infoPtr;    /* Pointer to control block. */
  472. {
  473.     Time sum;
  474.     static char result[20];
  475.  
  476.     Time_Add(infoPtr->userCpuUsage, infoPtr->kernelCpuUsage, &sum);
  477.     if (sum.microseconds >= 500000) {
  478.     sum.seconds += 1;
  479.     }
  480.     sprintf(result, "%d:%02d", sum.seconds/60, sum.seconds%60);
  481.     return result;
  482. }
  483.  
  484. /*
  485.  *----------------------------------------------------------------------
  486.  *
  487.  * StateString --
  488.  *
  489.  *    Given a process control block, return a string describing
  490.  *    the process's current execution state.
  491.  *
  492.  * Results:
  493.  *    The return value is a statically-allocated string that describes
  494.  *    the process's state.
  495.  *
  496.  * Side effects:
  497.  *    None.
  498.  *
  499.  *----------------------------------------------------------------------
  500.  */
  501.  
  502. char *
  503. StateString(infoPtr)
  504.     Proc_PCBInfo *infoPtr;    /* Pointer to control block. */
  505. {
  506.     switch (infoPtr->state) {
  507.     case PROC_UNUSED:
  508.         return "UNUSD";
  509.         break;
  510.     case PROC_RUNNING:
  511.         return "RUN  ";
  512.         break;
  513.     case PROC_READY:
  514.         return "READY";
  515.         break;
  516.     case PROC_WAITING:
  517.         if (infoPtr->event == -1) {
  518.         return "RWAIT";
  519.         } else {
  520.         return "WAIT ";
  521.         }
  522.         break;
  523.     case PROC_EXITING:
  524.         return "EXIT ";
  525.         break;
  526.     case PROC_DEAD:
  527.         return "DEAD ";
  528.         break;
  529.     case PROC_MIGRATING:
  530.         return "->MIG";
  531.         break;
  532.     case PROC_MIGRATED:
  533.         return "MIG  ";
  534.         break;
  535.     case PROC_NEW:
  536.         return "NEW  ";
  537.         break;
  538.     case PROC_SUSPENDED:
  539.         if (infoPtr->genFlags & (PROC_DEBUGGED | PROC_ON_DEBUG_LIST)) {
  540.         return "DEBUG";
  541.         } else {
  542.         return "SUSP ";
  543.         }
  544.         break;
  545.     }
  546.     return "?Huh?";
  547. }
  548.  
  549. /*
  550.  *----------------------------------------------------------------------
  551.  *
  552.  * ArgString --
  553.  *
  554.  *    Given an argument string for a command, modifies the string
  555.  *    to fit the line width for output.
  556.  *
  557.  * Results:
  558.  *    The return value is a pointer to the argument string.
  559.  *
  560.  * Side effects:
  561.  *    The argument string may be shortened by chopping off characters
  562.  *    and adding "...", if it is too long to fit on a single output line.
  563.  *
  564.  *----------------------------------------------------------------------
  565.  */
  566.  
  567. char *
  568. ArgString(argPtr, colsTaken)
  569.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  570.                  * process. */
  571.     int colsTaken;        /* Number of output columns already taken up
  572.                  * by other printed information. */
  573. {
  574.     int charsToKeep;
  575.     char *arg;
  576.     register char *p;
  577.  
  578.     arg = argPtr->argString;
  579.     charsToKeep = lineWidth - colsTaken;
  580.     if (charsToKeep < 10) {
  581.     charsToKeep = 10;
  582.     }
  583.     if (strlen(arg) <= charsToKeep) {
  584.     return arg;
  585.     }
  586.  
  587.     /*
  588.      * Chop fields off the command line until reaching something that fits
  589.      * within the line and leaves enough space for an ellipsis.  If not even
  590.      * the command name fits, then print a partial field.
  591.      */
  592.  
  593.     for (p = arg+charsToKeep-4; p > arg; p--) {
  594.     if (isspace(*p)) {
  595.         break;
  596.     }
  597.     }
  598.     if (p == arg) {
  599.     p = arg+charsToKeep-3;
  600.     } else {
  601.     for (p--; isspace(*p); p--) {
  602.         /* Null loop body;  just back over extra spaces. */
  603.     }
  604.     p += 2;
  605.     }
  606.     p[0] = '.';
  607.     p[1] = '.';
  608.     p[2] = '.';
  609.     p[3] = 0;
  610.     return arg;
  611. }
  612.  
  613. /*
  614.  *----------------------------------------------------------------------
  615.  *
  616.  * UserString --
  617.  *
  618.  *    Given a user id, return a string identifying the user.
  619.  *
  620.  * Results:
  621.  *    The return value is a pointer to a statically-allocated
  622.  *    string identifying the user.
  623.  *
  624.  * Side effects:
  625.  *    User information gets cached in a hash table.
  626.  *
  627.  *----------------------------------------------------------------------
  628.  */
  629.  
  630. char *
  631. UserString(uid)
  632.     int uid;            /* User id to find name for. */
  633. {
  634.     static Hash_Table table;
  635.     static int init = 0;
  636.     char *result;
  637.     register Hash_Entry *entry;
  638.     register struct passwd *passwd;
  639.     int new;
  640.  
  641.     if (!init) {
  642.     init = 1;
  643.     Hash_InitTable(&table, -1, HASH_ONE_WORD_KEYS);
  644.     }
  645.  
  646.     /*
  647.      * See if we've already looked up this process id.
  648.      */
  649.  
  650.     entry = Hash_CreateEntry(&table, (Address) uid, &new);
  651.     if (!new) {
  652.     return (char *) Hash_GetValue(entry);
  653.     }
  654.  
  655.     /*
  656.      * Never heard of this process id before.  Look it up in the
  657.      * password file and fill in the hash table entry.
  658.      */
  659.  
  660.     passwd = getpwuid(uid);
  661.     if (passwd == NULL) {
  662.     result = "???";
  663.     } else {
  664.     result = malloc((unsigned) (strlen(passwd->pw_name) + 1));
  665.     strcpy(result, passwd->pw_name);
  666.     }
  667.     Hash_SetValue(entry, result);
  668.     return result;
  669. }
  670.  
  671. /*
  672.  *----------------------------------------------------------------------
  673.  *
  674.  * HostString --
  675.  *
  676.  *    Given a host id, return a string identifying the host.
  677.  *
  678.  * Results:
  679.  *    The return value is a pointer to a statically-allocated
  680.  *    string identifying the host.
  681.  *
  682.  * Side effects:
  683.  *    Host information gets cached in a hash table.
  684.  *
  685.  *----------------------------------------------------------------------
  686.  */
  687.  
  688. char *
  689. HostString(hostID)
  690.     int hostID;            /* Host id to find name for. */
  691. {
  692.     static Hash_Table table;
  693.     static int init = 0;
  694.     char *result, *name;
  695.     register Hash_Entry *entry;
  696.     register Host_Entry *hostPtr;
  697.     char storage[20];
  698.     int new;
  699.  
  700.     if (!init) {
  701.     init = 1;
  702.     Hash_InitTable(&table, -1, HASH_ONE_WORD_KEYS);
  703.     }
  704.  
  705.     /*
  706.      * See if we've already looked up this host id.
  707.      */
  708.  
  709.     entry = Hash_CreateEntry(&table, (Address) hostID, &new);
  710.     if (!new) {
  711.     return (char *) Hash_GetValue(entry);
  712.     }
  713.  
  714.     /*
  715.      * Never heard of this host before.  Look it up in the
  716.      * host file and fill in the hash table entry.
  717.      */
  718.  
  719.     hostPtr = Host_ByID(hostID);
  720.     if (hostPtr == NULL) {
  721.     name = sprintf(storage, "%d", hostID);
  722.     } else {
  723.     if (hostPtr->aliases[0] != NULL) {
  724.         name = hostPtr->aliases[0];
  725.     } else {
  726.         name = hostPtr->name;
  727.     }
  728.     }
  729.     result = malloc((unsigned) (strlen(name) + 1));
  730.     strcpy(result, name);
  731.     Hash_SetValue(entry, result);
  732.     return result;
  733. }
  734.  
  735. /*
  736.  *----------------------------------------------------------------------
  737.  *
  738.  * PctCpuString --
  739.  *
  740.  *    Given an process table entry, return a string indicating what
  741.  *    fraction of recent CPU time has gone to this process.
  742.  *
  743.  * Results:
  744.  *    The return value is a pointer to a statically-allocated
  745.  *    string in the form "xx.y".  The string will change on the
  746.  *    next call to this procedure.
  747.  *
  748.  * Side effects:
  749.  *    None.
  750.  *
  751.  *----------------------------------------------------------------------
  752.  */
  753.  
  754. char *
  755. PctCpuString(infoPtr)
  756.     Proc_PCBInfo *infoPtr;    /* Pointer to control block. */
  757. {
  758.     static char result[10];
  759.     static int init = 0;
  760.     static double scaleFactor;
  761.     double percent;
  762.  
  763.     /*
  764.      * WARNING:  the following definitions must match the corresponding
  765.      * definitions in the kernel's file "schedule.c".
  766.      */
  767.  
  768. #define FORGET_MULTIPLY 14
  769. #define FORGET_SHIFT   4
  770.  
  771.     if (!init) {
  772.     int denom;
  773.  
  774.     init = 1;
  775.     scaleFactor = timer_IntOneSecond;
  776.     denom = 1<<FORGET_SHIFT;
  777.     scaleFactor *= denom;
  778.     scaleFactor /= denom-FORGET_MULTIPLY;
  779.     }
  780.  
  781.     percent = infoPtr->unweightedUsage;
  782.     percent = percent*100.0/scaleFactor;
  783.     sprintf(result, "%4.1f", percent);
  784.     return result;
  785. }
  786.  
  787. /*
  788.  *----------------------------------------------------------------------
  789.  *
  790.  * PrintShort --
  791.  *
  792.  *    This procedure is called to print out process information
  793.  *    in the "short" format.
  794.  *
  795.  * Results:
  796.  *    None.
  797.  *
  798.  * Side effects:
  799.  *    Prints info on standard output.
  800.  *
  801.  *----------------------------------------------------------------------
  802.  */
  803.  
  804. void
  805. PrintShort(infoPtr, argPtr, host)
  806.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  807.                  * info to be printed. */
  808.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  809.                  * process. */
  810.     int host;            /* Host on which process is running. */
  811. {
  812.     static int firstTime = 1;
  813.  
  814.     if (firstTime) {
  815.     firstTime = 0;
  816.     printf("PID   STATE   TIME COMMAND\n");
  817.     }
  818.     printf("%5x %s%7s %s\n", infoPtr->processID, StateString(infoPtr),
  819.         TimeString(infoPtr), ArgString(argPtr, 20));
  820. }
  821.  
  822. /*
  823.  *----------------------------------------------------------------------
  824.  *
  825.  * PrintLong --
  826.  *
  827.  *    This procedure is called to print out process information
  828.  *    in the "long" format (requested with the -u switch).
  829.  *
  830.  * Results:
  831.  *    None.
  832.  *
  833.  * Side effects:
  834.  *    Prints info on standard output.
  835.  *
  836.  *----------------------------------------------------------------------
  837.  */
  838.  
  839. void
  840. PrintLong(infoPtr, argPtr, host)
  841.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  842.                  * info to be printed. */
  843.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  844.                  * process. */
  845.     int host;            /* Host on which process is running. */
  846. {
  847.     static int firstTime = 1;
  848.     Vm_Stat vmStat;
  849.     Vm_SegmentInfo segBuf[VM_NUM_SEGMENTS];
  850.     ReturnStatus status;
  851.     int rss, size;
  852.     double pctMem;
  853.  
  854.     if (firstTime) {
  855.     firstTime = 0;
  856.     printf("USER     PID   %%CPU %%MEM  SIZE   RSS STATE   TIME COMMAND\n");
  857.     status = Vm_Cmd(VM_GET_STATS, &vmStat);
  858.     if (status != SUCCESS) {
  859.         fprintf(stderr, "Couldn't read Vm statistics: %s\n",
  860.             Stat_GetMsg(status));
  861.         exit(1);
  862.     }
  863.     }
  864.     if (infoPtr->genFlags & PROC_KERNEL) {
  865.     rss = size = 0;
  866.     } else {
  867.     status = Vm_GetSegInfo(infoPtr, 0, sizeof(Vm_SegmentInfo), 
  868.                       &(segBuf[1]));
  869.     switch(status) {
  870.         case SUCCESS:
  871.         rss = segBuf[VM_CODE].resPages + segBuf[VM_HEAP].resPages
  872.             + segBuf[VM_STACK].resPages;
  873.         size = segBuf[VM_CODE].numPages + segBuf[VM_HEAP].numPages
  874.             + segBuf[VM_STACK].numPages;
  875.         break;
  876.         case SYS_INVALID_ARG:
  877.         rss = -1;
  878.         break;
  879.         default: 
  880.         fprintf(stderr, "Couldn't read segment info for pid %x: %s\n",
  881.             infoPtr->processID, Stat_GetMsg(status));
  882.         return;
  883.     }
  884.     }
  885.     if (status == SUCCESS) {
  886.     pctMem = rss;
  887.     rss *= VMMACH_PAGE_SIZE/1024;
  888.     size *= VMMACH_PAGE_SIZE/1024;
  889.     pctMem = (pctMem*100.0)/vmStat.numPhysPages;
  890.     printf("%-8.8s %5x %.8s %4.1f%6d%6d %s%7s %s\n",
  891.         UserString(infoPtr->effectiveUserID),
  892.         infoPtr->processID, PctCpuString(infoPtr), pctMem, size, rss,
  893.         StateString(infoPtr), TimeString(infoPtr), 
  894.         ArgString(argPtr, 50));
  895.     } else {
  896.     printf("%-8.8s %5x %.8s %4s%6s%6s %s%7s %s\n",
  897.         UserString(infoPtr->effectiveUserID),
  898.         infoPtr->processID, PctCpuString(infoPtr),"---","---","---",
  899.         StateString(infoPtr), TimeString(infoPtr), 
  900.         ArgString(argPtr, 50));
  901.     }
  902. }
  903.  
  904. /*
  905.  *----------------------------------------------------------------------
  906.  *
  907.  * PrintIDs --
  908.  *
  909.  *    This procedure is called to print out process information
  910.  *    in the form of various ids.
  911.  *
  912.  * Results:
  913.  *    None.
  914.  *
  915.  * Side effects:
  916.  *    Prints info on standard output.
  917.  *
  918.  *----------------------------------------------------------------------
  919.  */
  920.  
  921. /* ARGSUSED */
  922. void
  923. PrintIDs(infoPtr, argPtr, host)
  924.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  925.                  * info to be printed. */
  926.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  927.                  * process. */
  928.     int host;            /* Host on which process is running. */
  929. {
  930.     static int firstTime = 1;
  931.     char storage[10];
  932.     char *family;
  933.  
  934.     if (firstTime) {
  935.     firstTime = 0;
  936.     printf("PID   PPID  GROUP USER     RUSER      TIME COMMAND\n");
  937.     }
  938.     if (infoPtr->familyID == -1) {
  939.     family = "   -1";
  940.     } else {
  941.     family = sprintf(storage, "%5x", infoPtr->familyID);
  942.     }
  943.     printf("%5x %5x %s %-8.8s %-8.8s%7s %s\n",
  944.         infoPtr->processID, infoPtr->parentID, family,
  945.         UserString(infoPtr->effectiveUserID), 
  946.         UserString(infoPtr->userID), TimeString(infoPtr),
  947.         ArgString(argPtr, 43));
  948. }
  949.  
  950. /*
  951.  *----------------------------------------------------------------------
  952.  *
  953.  * PrintVM --
  954.  *
  955.  *    This procedure is called to print out vm-related information
  956.  *    for processes.
  957.  *
  958.  * Results:
  959.  *    None.
  960.  *
  961.  * Side effects:
  962.  *    Prints info on standard output.
  963.  *
  964.  *----------------------------------------------------------------------
  965.  */
  966.  
  967. void
  968. PrintVM(infoPtr, argPtr, host)
  969.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  970.                  * info to be printed. */
  971.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  972.                  * process. */
  973.     int host;            /* Host on which process is running. */
  974. {
  975.     static int firstTime = 1;
  976.     static int sizes[VM_NUM_SEGMENTS], rss[VM_NUM_SEGMENTS];
  977.     static char *names[] = {"system", "code", "heap", "stack"};
  978.     int totalSize, totalRss, i;
  979.     Vm_SegmentInfo segBuf[VM_NUM_SEGMENTS];
  980.     ReturnStatus status;
  981. #define TOTAL_SEGS 256
  982.     char segSeen[TOTAL_SEGS];
  983.  
  984.     if (firstTime) {
  985.     firstTime = 0;
  986.     printf("PID   CODSZ CODRS  HPSZ  HPRS STKSZ STKRS  SIZE   RSS COMMAND\n");
  987.     }
  988.  
  989.     /*
  990.      * Kernel processes have no memory, so skip them.
  991.      */
  992.  
  993.     if (infoPtr->genFlags & PROC_KERNEL) {
  994.     return;
  995.     }
  996.     status = Vm_GetSegInfo(infoPtr, 0, sizeof(Vm_SegmentInfo), &(segBuf[1]));
  997.     if (status == SYS_INVALID_ARG) {
  998.     totalSize = -1;
  999.     } else if (status != SUCCESS) {
  1000.     fprintf(stderr, "Couldn't read segment info for pid %x: %s\n",
  1001.         infoPtr->processID, Stat_GetMsg(status));
  1002.     return;
  1003.     }
  1004.     if (status == SUCCESS) {
  1005.     totalSize = totalRss = 0;
  1006.     for (i = VM_CODE; i < VM_NUM_SEGMENTS; i++) {
  1007.         totalSize += segBuf[i].numPages*(VMMACH_PAGE_SIZE/1024);
  1008.         totalRss += segBuf[i].resPages*(VMMACH_PAGE_SIZE/1024);
  1009.         if (segBuf[i].segNum >= TOTAL_SEGS) {
  1010.         fprintf(stderr, "Pid %x has %s segment %d:  too large.\n",
  1011.             infoPtr->processID, names[i], segBuf[i].segNum);
  1012.         continue;
  1013.         }
  1014.         if (segSeen[segBuf[i].segNum]) {
  1015.         continue;
  1016.         }
  1017.         segSeen[segBuf[i].segNum] = 1;
  1018.         sizes[i] += segBuf[i].numPages*(VMMACH_PAGE_SIZE/1024);
  1019.         rss[i] += segBuf[i].resPages*(VMMACH_PAGE_SIZE/1024);
  1020.     }
  1021.     printf("%5x%6d%6d%6d%6d%6d%6d%6d%6d %s\n",
  1022.         infoPtr->processID,
  1023.         segBuf[VM_CODE].numPages*(VMMACH_PAGE_SIZE/1024),
  1024.         segBuf[VM_CODE].resPages*(VMMACH_PAGE_SIZE/1024),
  1025.         segBuf[VM_HEAP].numPages*(VMMACH_PAGE_SIZE/1024),
  1026.         segBuf[VM_HEAP].resPages*(VMMACH_PAGE_SIZE/1024),
  1027.         segBuf[VM_STACK].numPages*(VMMACH_PAGE_SIZE/1024),
  1028.         segBuf[VM_STACK].resPages*(VMMACH_PAGE_SIZE/1024),
  1029.         totalSize, totalRss, ArgString(argPtr, 54));
  1030.     } else {
  1031.     printf("%5x%6s%6s%6s%6s%6s%6s%6s%6s %s\n",
  1032.         infoPtr->processID,"---","---","---","---","---","---",
  1033.         "---","---", ArgString(argPtr, 54));
  1034.     }
  1035.     if (lastPCB) {
  1036.     printf("-----------------------------------------------------\n");
  1037.     printf("Total%6d%6d%6d%6d%6d%6d%6d%6d\n",
  1038.         sizes[VM_CODE], rss[VM_CODE], sizes[VM_HEAP], rss[VM_HEAP],
  1039.         sizes[VM_STACK], rss[VM_STACK],
  1040.         sizes[VM_CODE] + sizes[VM_HEAP] + sizes[VM_STACK],
  1041.         rss[VM_CODE] + rss[VM_HEAP] + rss[VM_STACK]);
  1042.     }
  1043. }
  1044.  
  1045. /*
  1046.  *----------------------------------------------------------------------
  1047.  *
  1048.  * PrintMigration --
  1049.  *
  1050.  *    This procedure is called to print out migration-related information
  1051.  *    for processes.
  1052.  *
  1053.  * Results:
  1054.  *    None.
  1055.  *
  1056.  * Side effects:
  1057.  *    Prints info on standard output.
  1058.  *
  1059.  *----------------------------------------------------------------------
  1060.  */
  1061.  
  1062. /* ARGSUSED */
  1063. void
  1064. PrintMigration(infoPtr, argPtr, host)
  1065.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  1066.                  * info to be printed. */
  1067.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  1068.                  * process. */
  1069.     int host;            /* Host on which process is running. */
  1070. {
  1071.     static int firstTime = 1;
  1072.  
  1073.     if (firstTime) {
  1074.     firstTime = 0;
  1075.     printf("PID   STATE   FLAGS    EVENT RNODE       RPID COMMAND\n");
  1076.     }
  1077.     if ((infoPtr->genFlags & PROC_FOREIGN)
  1078.         || (infoPtr->state == PROC_MIGRATED)) {
  1079.     printf("%5x %s%8x %8x %-10.10s %5x %s\n",
  1080.         infoPtr->processID, StateString(infoPtr),
  1081.         infoPtr->genFlags, infoPtr->event,
  1082.         HostString(infoPtr->peerHostID),
  1083.         infoPtr->peerProcessID,
  1084.         ArgString(argPtr, 46));
  1085.     } else {
  1086.     printf("%5x %s%8x %8x                  %s\n",
  1087.         infoPtr->processID, StateString(infoPtr),
  1088.         infoPtr->genFlags, infoPtr->event,
  1089.         ArgString(argPtr, 46));
  1090.     }
  1091. }
  1092.  
  1093. /*
  1094.  *----------------------------------------------------------------------
  1095.  *
  1096.  * PrintSignals --
  1097.  *
  1098.  *    This procedure is called to print out signal-related information
  1099.  *    for processes.
  1100.  *
  1101.  * Results:
  1102.  *    None.
  1103.  *
  1104.  * Side effects:
  1105.  *    Prints info on standard output.
  1106.  *
  1107.  *----------------------------------------------------------------------
  1108.  */
  1109.  
  1110. /* ARGSUSED */
  1111. void
  1112. PrintSignals(infoPtr, argPtr, host)
  1113.     Proc_PCBInfo *infoPtr;    /* Pointer to control block containing
  1114.                  * info to be printed. */
  1115.     Proc_PCBArgString *argPtr;    /* Pointer to info about command line for
  1116.                  * process. */
  1117.     int host;            /* Host on which process is running. */
  1118. {
  1119.     static int firstTime = 1;
  1120.     int ignore, handle, i;
  1121.  
  1122.     if (firstTime) {
  1123.     firstTime = 0;
  1124.     printf("PID    PENDING     HELD   IGNORE   HANDLE COMMAND\n");
  1125.     }
  1126.  
  1127.     ignore = handle = 0;
  1128.     for (i = 1; i <= SIG_NUM_SIGNALS; i++) {
  1129.     if (infoPtr->sigActions[i] == SIG_IGNORE_ACTION) {
  1130.         ignore |= 1<<(i-1);
  1131.     } else if (infoPtr->sigActions[i] >> SIG_NUM_ACTIONS) {
  1132.         handle |= 1<<(i-1);
  1133.     }
  1134.     }
  1135.     printf("%5x %8x %8x %8x %8x %s\n", infoPtr->processID,
  1136.         infoPtr->sigPendingMask, infoPtr->sigHoldMask,
  1137.         ignore, handle, ArgString(argPtr, 42));
  1138. }
  1139.  
  1140. /*
  1141.  *----------------------------------------------------------------------
  1142.  *
  1143.  * UsageSort --
  1144.  *
  1145.  *    This procedure is called while sorting the process table
  1146.  *    entries.  It returns a value that will sort the processes
  1147.  *    in decreasing order of recent CPU usage.
  1148.  *
  1149.  * Results:
  1150.  *    Returns < 0 if first's usage is > second's usage, >0 otherwise.
  1151.  *
  1152.  * Side effects:
  1153.  *    None.
  1154.  *
  1155.  *----------------------------------------------------------------------
  1156.  */
  1157.  
  1158. int
  1159. UsageSort(first, second)
  1160.     ControlBlock *first, *second;    /* Two PCBs to compare. */
  1161. {
  1162.     int i;
  1163.     i = second->infoPtr->unweightedUsage - first->infoPtr->unweightedUsage;
  1164.     return i;
  1165. }
  1166.  
  1167. /*
  1168.  *----------------------------------------------------------------------
  1169.  *
  1170.  * AgeSort --
  1171.  *
  1172.  *    This procedure is called while sorting the process table
  1173.  *    entries.  It attempts to return a value that will sort
  1174.  *    processes by age.
  1175.  *
  1176.  * Results:
  1177.  *    Returns < 0 if first is older than second.
  1178.  *
  1179.  * Side effects:
  1180.  *    None.
  1181.  *
  1182.  *----------------------------------------------------------------------
  1183.  */
  1184.  
  1185. int
  1186. AgeSort(first, second)
  1187.     ControlBlock *first, *second;    /* Two PCBs to compare. */
  1188. {
  1189. #ifdef NOTDEF
  1190.     Time    firstTime, secondTime;
  1191.  
  1192.     /*
  1193.      * Unfortunately there's no direct indicator of age in the PCB.
  1194.      * Instead, use the total time used by the process and all its
  1195.      * children as a crude approximation.
  1196.      */
  1197.  
  1198.     Time_Add(first->infoPtr->kernelCpuUsage,
  1199.          first->infoPtr->userCpuUsage, &firstTime);
  1200.     Time_Add(first->infoPtr->childKernelCpuUsage,
  1201.           firstTime, &firstTime);
  1202.     Time_Add(first->infoPtr->childUserCpuUsage,
  1203.         firstTime, &firstTime);
  1204.     Time_Add(second->infoPtr->kernelCpuUsage,
  1205.         second->infoPtr->userCpuUsage, &secondTime);
  1206.     Time_Add(second->infoPtr->childKernelCpuUsage,
  1207.         secondTime, &secondTime);
  1208.     Time_Add(second->infoPtr->childUserCpuUsage,
  1209.         secondTime, &secondTime);
  1210.     if Time_GT(secondTicks, firstTime) {
  1211.     return 1;
  1212.     } else {
  1213.     return -1;
  1214.     }
  1215. #endif
  1216.     return second->infoPtr->numWaitEvents - first->infoPtr->numWaitEvents;
  1217. }
  1218.